package com.hifar.test.syncData.service.impl;

import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hifar.constant.SystemConsts;
import com.hifar.fw.json.JsonUtils;
import com.hifar.fw.utils.lang.CollectionUtils;
import com.hifar.fw.utils.lang.DateUtils;
import com.hifar.fw.utils.lang.StringUtils;
import com.hifar.plat.baseModal.service.IBaseOssFilesService;
import com.hifar.sys.MinioUtil;
import com.hifar.test.syncData.constant.SyncDataConstant;
import com.hifar.test.syncData.enums.SyncDataStatusEnum;
import com.hifar.test.syncData.mapper.HfEnvSyncDataExportTaskMapper;
import com.hifar.test.syncData.pojo.HfEnvSyncDataExportTask;
import com.hifar.test.syncData.pojo.HfEnvSyncDataExportTableInfo;
import com.hifar.test.syncData.pojo.HfEnvSyncTableConfig;
import com.hifar.test.syncData.pojo.model.EnvSyncDataAuthModel;
import com.hifar.test.syncData.service.ICollectDataSourceService;
import com.hifar.test.syncData.service.IHfEnvSyncDataExportTableInfoService;
import com.hifar.test.syncData.service.IHfEnvSyncDataExportTaskService;
import com.hifar.test.syncData.service.IHfEnvSyncTableConfigService;
import com.hifar.test.syncData.service.IMasterDataSourceService;
import com.hifar.utils.AesUtils;
import com.hifar.utils.FileUtil;
import com.hifar.utils.ToolKit;
import com.tssk.kylx.fw.utils.id.IdUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author zhuWeiWei
 * @description 导入任务记录
 * @date 2025/4/11 12:00
 */
@Service
@Slf4j
public class HfEnvSyncDataExportTaskServiceImpl extends ServiceImpl<HfEnvSyncDataExportTaskMapper, HfEnvSyncDataExportTask>
    implements IHfEnvSyncDataExportTaskService {

    @Autowired
    private IMasterDataSourceService masterDataSourceService;
    @Autowired
    private ICollectDataSourceService collectDataSourceService;
    @Autowired
    private IHfEnvSyncTableConfigService syncTableConfigService;
    @Autowired
    private IBaseOssFilesService baseOssFilesService;
    @Autowired
    private IHfEnvSyncDataExportTableInfoService exportTableInfoService;


    @Override
    public void updateSchedule(String taskId, BigDecimal schedule, String status) {
        baseMapper.updateSchedule(taskId, schedule, status);
    }

    @Override
    public void taskFinish(String taskId, String bucketPre, String filePath) {
        UpdateWrapper<HfEnvSyncDataExportTask> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .set(HfEnvSyncDataExportTask::getEndTime, new Date())
                .set(HfEnvSyncDataExportTask::getBucketPre, bucketPre)
                .set(HfEnvSyncDataExportTask::getFilePath, filePath)
                .set(HfEnvSyncDataExportTask::getStatus, SyncDataStatusEnum.SUCCESS.getValue())
                .set(HfEnvSyncDataExportTask::getSchedule, new BigDecimal("100"))
                .eq(HfEnvSyncDataExportTask::getId, taskId);
        this.update(updateWrapper);
    }

    @Override
    public void taskError(String taskId, String remarks) {
        UpdateWrapper<HfEnvSyncDataExportTask> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .set(HfEnvSyncDataExportTask::getEndTime, new Date())
                .set(HfEnvSyncDataExportTask::getStatus, SyncDataStatusEnum.ERROR.getValue())
//                .set(HfEnvSyncDataExportTask::getSchedule, BigDecimal.ZERO)
                .set(HfEnvSyncDataExportTask::getRemarks, remarks)
                .eq(HfEnvSyncDataExportTask::getId, taskId);
        this.update(updateWrapper);
    }

    @Override
    public void syncTableData(String taskId, HfEnvSyncTableConfig syncTableConfig) {
        // 参数校验
        if (syncTableConfig == null) {
            log.error("【导出失败】表配置为空，任务ID：{}", taskId);
            throw new IllegalArgumentException("表配置不能为空");
        }

        String tableName = syncTableConfig.getTableName();
        String description = syncTableConfig.getDescription();
        String dataSource = syncTableConfig.getDataSource();

        if (StringUtils.isBlank(tableName)) {
            log.error("【导出失败】表名称为空，任务ID：{}", taskId);
            throw new IllegalArgumentException("表名称不能为空");
        }

        log.info("═══════════════════════════════════════════════════");
        log.info("【开始导出表】表名：{}，描述：{}，数据源：{}", tableName, description, dataSource);
        log.info("═══════════════════════════════════════════════════");

        HfEnvSyncDataExportTask hfEnvSyncDataExportTask = getById(taskId);
        Assert.notNull(hfEnvSyncDataExportTask, "导出任务不存在!");

        String tempPath = hfEnvSyncDataExportTask.getTempPath();
        String tempTableDataPath = tempPath + File.separator + SyncDataConstant.DB_DATA_PATH;
        String tempTableFilePath = tempPath + File.separator + SyncDataConstant.FILE_DATA_PATH;

        log.debug("【路径信息】临时目录：{}", tempPath);
        log.debug("【路径信息】数据目录：{}", tempTableDataPath);
        log.debug("【路径信息】文件目录：{}", tempTableFilePath);

        File dbDataBaseFile = new File(tempTableDataPath);
        if (!dbDataBaseFile.exists()) {
            boolean created = dbDataBaseFile.mkdirs();
            log.debug("【目录创建】数据目录创建{}：{}", created ? "成功" : "失败", tempTableDataPath);
        }

        File tableDataFile = new File(tempTableDataPath + File.separator + tableName + ".json");

        try {
            // 查询表数据
            log.info("【查询数据】开始查询表【{}】的数据...", tableName);
            List<Map<String, Object>> dataList = queryDataByTable(syncTableConfig);

            if (Objects.isNull(dataList) || dataList.isEmpty()) {
                log.warn("【查询结果】表【{}】没有数据，跳过导出，不记录到数据库", tableName);
                return;
            }

            int recordNum = dataList.size();
            log.info("【查询结果】表【{}】查询到 {} 条数据", tableName, recordNum);

            // 写入JSON文件
            log.debug("【写入文件】开始写入JSON文件：{}", tableDataFile.getAbsolutePath());
            String dataJson = JsonUtils.toJson(dataList);
            ToolKit.writeFile(tableDataFile, dataJson, "UTF-8");
            long fileSize = tableDataFile.length();
            log.info("【写入文件】文件写入成功，大小：{} KB", fileSize / 1024);

            // 导出关联附件
            int fileSuccessCount = 0;
            int fileFailCount = 0;
            int totalFileCount = 0;

            String izFile = syncTableConfig.getIzFile();
            String fileFields = syncTableConfig.getFileFields();
            String bucketPreField = syncTableConfig.getBucketPre();
            List<String> fileFieldList = CollectionUtils.valueOfList(fileFields);

            log.debug("【附件配置检查】表【{}】- izFile: {}, fileFields: {}, bucketPreField: {}",
                tableName, izFile, fileFields, bucketPreField);

            if (Objects.equals(SystemConsts.YES, izFile) && fileFieldList != null && !fileFieldList.isEmpty()) {
                log.info("【导出附件】开始导出表【{}】的关联附件，字段列表：{}", tableName, fileFields);

                // 记录第一条数据的所有key，用于诊断
                if (!dataList.isEmpty() && dataList.get(0) != null) {
                    Map<String, Object> firstRecord = dataList.get(0);
                    log.debug("【字段诊断】表【{}】查询结果的字段列表：{}", tableName, firstRecord.keySet());
                }

                for (Map<String, Object> dataMap : dataList) {
                    if (dataMap == null) {
                        continue;
                    }

                    for (String filePathField : fileFieldList) {
                        // 智能匹配字段：尝试多种格式
                        // 1. 标准驼峰转换（先转小写再转驼峰）
                        String standardCamelCase = ToolKit.snakeToCamel(filePathField.toLowerCase());
                        // 2. 直接转换（可能不正确，但兼容旧逻辑）
                        String directCamelCase = ToolKit.snakeToCamel(filePathField);
                        // 3. 原始字段名
                        String originalField = filePathField;
                        // 4. 小写字段名
                        String lowerField = filePathField.toLowerCase();
                        
                        String filePath = null;
                        String matchedKey = null;
                        
                        // 按优先级尝试各种格式
                        if (dataMap.containsKey(standardCamelCase)) {
                            filePath = StringUtils.trimNull(dataMap.get(standardCamelCase));
                            matchedKey = standardCamelCase;
                        } else if (dataMap.containsKey(directCamelCase)) {
                            filePath = StringUtils.trimNull(dataMap.get(directCamelCase));
                            matchedKey = directCamelCase;
                        } else if (dataMap.containsKey(originalField)) {
                            filePath = StringUtils.trimNull(dataMap.get(originalField));
                            matchedKey = originalField;
                        } else if (dataMap.containsKey(lowerField)) {
                            filePath = StringUtils.trimNull(dataMap.get(lowerField));
                            matchedKey = lowerField;
                        }

                        if (StringUtils.isBlank(filePath)) {
                            log.trace("【附件字段】记录中字段【{}】无值，尝试过的key: [{}, {}, {}, {}]，实际key: {}",
                                filePathField, standardCamelCase, directCamelCase, originalField, lowerField, 
                                dataMap.keySet());
                            continue;
                        }
                        
                        log.debug("【附件字段】字段【{}】匹配成功，使用key: {}，文件路径: {}", 
                                filePathField, matchedKey, filePath);

                        totalFileCount++;
                        String bucketPre = StringUtils.isNotBlank(bucketPreField)
                            ? StringUtils.trimNull(dataMap.get(ToolKit.snakeToCamel(bucketPreField)), MinioUtil.PRE_DEFAULT)
                            : MinioUtil.PRE_DEFAULT;

                        try {
                            // 安全处理文件路径
                            int lastSlashIndex = filePath.lastIndexOf("/");
                            if (lastSlashIndex <= 0) {
                                log.warn("【导出附件】文件路径格式不正确，跳过，路径：{}", filePath);
                                fileFailCount++;
                                continue;
                            }

                            String fileBasePath = filePath.substring(0, lastSlashIndex);
                            String downloadTempBasePath = tempTableFilePath + File.separator + bucketPre + File.separator + fileBasePath;
                            File downloadTempBaseDirFile = new File(downloadTempBasePath);
                            if (!downloadTempBaseDirFile.exists()) {
                                downloadTempBaseDirFile.mkdirs();
                            }

                            String downloadTempFilePath = tempTableFilePath + File.separator + bucketPre + File.separator + filePath;
                            log.debug("【导出附件】下载文件：{}/{}", bucketPre, filePath);
                            MinioUtil.downloadFileToLocal(bucketPre, filePath, downloadTempFilePath);
                            fileSuccessCount++;
                        } catch (Exception e) {
                            fileFailCount++;
                            log.error("【导出附件】文件下载失败，表：{}，文件路径：{}，错误：{}", tableName, filePath, e.getMessage());
                        }
                    }
                }

                log.info("【导出附件】表【{}】附件导出完成，总计：{}个，成功：{}个，失败：{}个",
                    tableName, totalFileCount, fileSuccessCount, fileFailCount);
            } else {
                // 详细说明为什么没有导出附件
                if (fileFieldList == null || fileFieldList.isEmpty()) {
                    log.warn("【导出附件】表【{}】跳过附件导出 - 原因：file_fields字段未配置或为空（当前值：{}）", tableName, fileFields);
                } else {
                    log.debug("【导出附件】表【{}】无需导出附件", tableName);
                }
            }

            // 保存表导出成功信息（包含附件统计）
            saveTableInfo(taskId, tableName, description, dataSource, recordNum, "1", null,
                totalFileCount, fileSuccessCount, fileFailCount);
            log.info("【保存记录】表【{}】导出信息已保存到数据库", tableName);

            log.info("═══════════════════════════════════════════════════");
            log.info("【导出完成】表【{}】导出成功！记录数：{}，附件：{}/{}", tableName, recordNum, fileSuccessCount, totalFileCount);
            log.info("═══════════════════════════════════════════════════");

        } catch (Exception e) {
            log.error("═══════════════════════════════════════════════════");
            log.error("【导出失败】表【{}】导出失败，错误：{}", tableName, e.getMessage(), e);
            log.error("═══════════════════════════════════════════════════");
            // 保存表导出失败信息
            saveTableInfo(taskId, tableName, description, dataSource, 0, "2", e.getMessage(), 0, 0, 0);
            throw new RuntimeException("表【" + tableName + "】导出失败: " + e.getMessage(), e);
        }
    }

    /**
     * 保存表导出信息到数据库
     *
     * @param taskId      任务ID
     * @param tableName   表名称
     * @param description 表描述
     * @param dataSource  数据源
     * @param recordNum   记录数量
     * @param status      状态：1-成功 2-失败
     * @param errorMsg    错误信息
     * @param fileTotal   附件总数
     * @param fileSuccess 附件成功数
     * @param fileFail    附件失败数
     */
    private void saveTableInfo(String taskId, String tableName, String description,
                               String dataSource, Integer recordNum, String status, String errorMsg,
                               Integer fileTotal, Integer fileSuccess, Integer fileFail) {
        try {
            HfEnvSyncDataExportTableInfo tableInfo = new HfEnvSyncDataExportTableInfo();
            tableInfo.setTaskId(taskId);
            tableInfo.setTableName(tableName);
            tableInfo.setDescription(description);
            tableInfo.setDataSource(dataSource);
            tableInfo.setRecordNum(recordNum);
            tableInfo.setStatus(status); // 1-成功 2-失败
            tableInfo.setRemarks(errorMsg);
            tableInfo.setFileTotal(fileTotal);
            tableInfo.setFileSuccess(fileSuccess);
            tableInfo.setFileFail(fileFail);
            exportTableInfoService.save(tableInfo);
            log.debug("【数据库记录】表信息保存成功 - 表名：{}，状态：{}，记录数：{}，附件：{}/{}",
                tableName, status, recordNum, fileSuccess, fileTotal);
        } catch (Exception e) {
            log.error("【数据库记录】保存表信息失败，表名：{}，错误：{}", tableName, e.getMessage(), e);
        }
    }

    @Override
    public List<Map<String, Object>> queryDataByTable(HfEnvSyncTableConfig syncTableConfig) {
        String dataSource = syncTableConfig.getDataSource();
        String enable = syncTableConfig.getEnable();
        String id = syncTableConfig.getId();
        if (Objects.equals(SystemConsts.NO, enable)) {
            return null;
        }
        Date syncDate = new Date();
        List<Map<String, Object>> dataList = null;
        if (Objects.equals("master", dataSource)) {
            dataList = masterDataSourceService.listTableData(syncTableConfig);
        } else {
            dataList = collectDataSourceService.listTableData(syncTableConfig);
        }
        syncTableConfigService.updateLastTime(id, syncDate);
        return dataList;
    }

    @Override
    public void syncFile(String taskId) {
        log.info("开始同步导出附件，任务ID：{}", taskId);

        HfEnvSyncDataExportTask syncDataExportTask = this.getById(taskId);
        if (syncDataExportTask == null) {
            log.error("导出任务不存在，任务ID：{}", taskId);
            return;
        }

        String syncCode = syncDataExportTask.getSyncCode();
        String tempPath = syncDataExportTask.getTempPath();

        if (StringUtils.isBlank(tempPath)) {
            log.error("临时路径为空，任务ID：{}", taskId);
            this.taskError(taskId, "临时路径为空");
            return;
        }

        try {
            // 生成认证文件
            EnvSyncDataAuthModel envSyncDataAuthModel = new EnvSyncDataAuthModel();
            envSyncDataAuthModel.setSyncCode(syncCode);
            File authFile = new File(tempPath + File.separator + SyncDataConstant.AUTH_FILE_NAME);
            String authText = JsonUtils.toJson(envSyncDataAuthModel);
            ToolKit.writeFile(authFile, authText, "UTF-8");
            log.info("认证文件生成成功，同步编码：{}", syncCode);

            // 压缩打包上传
            this.encryptionSyncExportFile(taskId);
        } catch (Exception e) {
            log.error("同步导出附件失败，任务ID：{}，错误：{}", taskId, e.getMessage(), e);
            this.taskError(taskId, "同步附件失败: " + e.getMessage());
        }
    }

    @Override
    public void encryptionSyncExportFile(String taskId) {
        log.info("开始压缩打包导出文件，任务ID：{}", taskId);

        HfEnvSyncDataExportTask syncDataExportTask = this.getById(taskId);
        if (syncDataExportTask == null) {
            log.error("导出任务不存在，任务ID：{}", taskId);
            return;
        }

        String tempPath = syncDataExportTask.getTempPath();
        if (StringUtils.isBlank(tempPath)) {
            log.error("临时路径为空，任务ID：{}", taskId);
            this.taskError(taskId, "临时路径为空");
            return;
        }

        String encryptZipPath = FileUtil.getTempPath() + File.separator + IdUtils.uuid32() + ".zip";
        File zip = null;
        File tempDir = new File(tempPath);

        try {
            // 验证临时目录是否存在
            if (!tempDir.exists()) {
                log.error("临时目录不存在，任务ID：{}，路径：{}", taskId, tempPath);
                this.taskError(taskId, "临时目录不存在");
                return;
            }

            // 压缩文件
            zip = ZipUtil.zip(tempPath);
            log.info("压缩完成，任务ID：{}，文件大小：{}KB", taskId, zip.length() / 1024);

            // 加密功能（当前被注释）
//            AesUtils.encryptFile(SystemConsts.AES_KEY, zip.getAbsolutePath(), encryptZipPath);

            // 上传到MinIO
            String fileName = String.format("数据包_%s", DateUtils.formatTime(new Date(), "yyyyMMddHHmmss"));
            String exportPath = "hifar/syncFile/" + fileName + ".zip";
            MinioUtil.uploadLocalFileToMinio(MinioUtil.PRE_DEFAULT, exportPath, zip.getAbsolutePath());
            log.info("文件上传成功，任务ID：{}，路径：{}", taskId, exportPath);

            // 标记任务完成
            this.taskFinish(taskId, MinioUtil.PRE_DEFAULT, exportPath);
            log.info("数据导出任务完成，任务ID：{}", taskId);

        } catch (Exception e) {
            log.error("压缩打包失败，任务ID：{}，错误：{}", taskId, e.getMessage(), e);
            this.taskError(taskId, "压缩打包失败: " + e.getMessage());
        } finally {
            // 清理临时文件
            try {
                if (Objects.nonNull(zip) && zip.exists()) {
                    boolean deleted = zip.delete();
                    log.debug("ZIP文件清理{}，任务ID：{}", deleted ? "成功" : "失败", taskId);
                }

                File encryptZip = new File(encryptZipPath);
                if (encryptZip.exists()) {
                    boolean deleted = encryptZip.delete();
                    log.debug("加密ZIP文件清理{}，任务ID：{}", deleted ? "成功" : "失败", taskId);
                }

                if (tempDir.exists()) {
                    FileUtils.deleteDirectory(tempDir);
                    log.info("临时目录清理成功，任务ID：{}", taskId);
                }
            } catch (IOException e) {
                log.error("清理临时文件失败，任务ID：{}，错误：{}", taskId, e.getMessage());
            }
        }
    }
}
